package com.zc.manager.device.domain;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.annotation.FieldStrategy;
import com.baomidou.mybatisplus.annotation.TableField;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.ruoyi.common.annotation.Excel;
import com.zc.common.core.validation.ValidJson;
import com.zc.core.device.DeviceConfigKey;
import com.zc.core.device.DeviceInfo;
import com.zc.core.metadata.DeviceMetadata;
import com.zc.core.metadata.MergeOption;
import com.zc.supports.official.JetLinksDeviceMetadataCodec;
import lombok.Data;
import org.hswebframework.ezorm.rdb.mapping.annotation.DefaultValue;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import reactor.core.publisher.Mono;

import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Pattern;
import javax.validation.constraints.Size;
import java.util.*;

/**
 * 设备实体类
 *
 * @author zhaoxianglong
 * @date 2022-11-7
 */
@Data
public class IotDevice {

    public static final String OFFLINE = "offline";
    public static final String ONLINE = "online";
    public static final String NOT_ACTIVE = "notActive";

    /**
     * 主键
     */
    @Excel(name = "设备ID")
    @NotBlank(message = "设备ID不能为空")
    @Size(max = 50, message = "设备ID长度不能超过50个字符")
    private String id;

    /**
     * 网关设备(网关子设备需要)
     */
    @Excel(name = "网关设备(父设备ID)")
    @TableField(updateStrategy = FieldStrategy.IGNORED)
    private String parentId;

    /**
     * 所属产品
     */
    @Excel(name = "所属产品(产品ID)")
    @NotBlank(message = "产品ID(所属产品)不能为空")
    @Size(max = 50, message = "产品ID(所属产品)长度不能超过50个字符")
    private String productId;

    /**
     * 所属分组(保留)
     */
    // @Excel(name = "所属分组")
    private Long groupId;

    /**
     * 其他配置
     */
    @Excel(name = "其他配置")
    @ValidJson(message = "拓展配置不是有效JSON数据")
    private String configuration;

    /**
     * 设备名称
     */
    @Excel(name = "设备名称")
    @NotBlank(message = "设备名称不能为空")
    @Size(max = 100, message = "设备名称长度不能超过100个字符")
    private String deviceName;

    /**
     * 设备编号
     */
    @Excel(name = "设备编号")
    @Size(max = 50, message = "设备编号长度不能超过50个字符")
    private String deviceCode;

    /**
     * 设备状态
     *  离线(offline)
     *  在线(online)
     *  未激活(notActive)
     */
    @Excel(name = "设备状态",  type = Excel.Type.EXPORT, readConverterExp = "offline=离线,online=在线,notActive=未激活")
    @Pattern(regexp = "offline|online|notActive", message = "无效的设备状态")
    private String deviceState;

    /**
     * 设备特性
     * @see com.zc.manager.device.enums.DeviceFeature
     */
    @DefaultValue("0")
    private Integer features;

    /**
     * 注册时间
     */
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone="GMT+8")
    @Excel(name = "注册时间", type = Excel.Type.EXPORT, dateFormat = "yyyy-MM-dd HH:mm:ss")
    private Date registryTime;

    /**
     * 派生物模型
     */
    @Excel(name = "派生物模型")
    @ValidJson(message = "派生物模型不是有效JSON数据")
    private String deriveMetadata;

    /** 协议支持 */
    @Excel(name = "协议支持")
    @ValidJson(message = "协议支持不是有效JSON数据")
    private String protocolStandby;

    /**
     * 设备地址
     */
    @Excel(name = "设备网络地址(/ip:port)")
    //@Pattern(regexp = "^$|/(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])\\.(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])\\.(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])\\.(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5]):([0-9]|[1-9]\\d|[1-9]\\d{2}|[1-9]\\d{3}|[1-5]\\d{4}|6[0-4]\\d{3}|65[0-4]\\d{2}|655[0-2]\\d|6553[0-5])$",
    //        message = "无效的设备地址")
    private String address;

    /**
     * 描述
     */
    @Excel(name = "描述")
    @Size(max = 200, message = "设备描述长度不能超过200个字符")
    private String description;

    /**
     * 创建时间
     */
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone="GMT+8")
    @Excel(name = "创建时间", dateFormat = "yyyy-MM-dd HH:mm:ss", type = Excel.Type.EXPORT)
    private Date createTime;

    /**
     * 更新时间
     */
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone="GMT+8")
    @Excel(name = "更新时间", dateFormat = "yyyy-MM-dd HH:mm:ss", type = Excel.Type.EXPORT)
    private Date updateTime;

    /**
     * 设备类型
     */
    @TableField(exist = false)
    private String deviceType;

    /**
     * 子设备id
     */
    @TableField(exist = false)
    private Set<String> childrenId;


    public DeviceInfo toDeviceInfo() {
        DeviceInfo info = DeviceInfo
                .builder()
                .id(this.getId())
                .productId(this.getProductId())
                .build()
                // 网关设备id
                .addConfig(DeviceConfigKey.parentGatewayId, this.getParentId())
                .addConfig("childrenId", this.childrenId);

        info.addConfig("deviceName", deviceName);
        info.addConfig("deviceCode", deviceCode);

        if (hasFeature()) {
            info.addConfig(DeviceConfigKey.selfManageState, true);
        }

        JSONObject configuration = JSONObject.parseObject(this.configuration);

       if (!CollectionUtils.isEmpty(configuration)) {
            info.addConfigs(configuration);
        }

        if (StringUtils.hasText(deriveMetadata)) {
            info.setMetadata(deriveMetadata);
        }

        if (StringUtils.hasText(protocolStandby)) {
            info.setProtocolStandby(IotProduct.toProtocolStandbyValue(protocolStandby));
        }

        return info;
    }

    public Mono<String> mergeMetadata(String metadata) {
        return JetLinksDeviceMetadataCodec
                .getInstance()
                .decode(metadata)
                .flatMap(this::mergeMetadata);
    }

    public Mono<String> mergeMetadata(DeviceMetadata metadata) {
        JetLinksDeviceMetadataCodec codec = JetLinksDeviceMetadataCodec.getInstance();

        if (StringUtils.isEmpty(this.getDeriveMetadata())) {
            return codec.encode(metadata)
                    .doOnNext(this::setDeriveMetadata);
        }

        return Mono
                .zip(
                        codec.decode(getDeriveMetadata()),
                        Mono.just(metadata),
                        (derive, another) -> derive.merge(another, MergeOption.ignoreExists)
                )
                .flatMap(codec::encode)
                .doOnNext(this::setDeriveMetadata);
    }

    public void mergeConfiguration(Map<String, Object> configuration) {

        Map<String, Object> newConf = new HashMap<>(configuration);
        //状态自管理，单独设置到feature中
        Object selfManageState = newConf.remove(DeviceConfigKey.selfManageState.getKey());
        if (null != selfManageState) {
             if (Boolean.TRUE.equals(selfManageState)) {
                 this.features = 1;
             } else {
                 this.features = 0;
             }
         }
        //物模型单独设置
        Object metadata = newConf.remove(DeviceConfigKey.metadata.getKey());
        if (null != metadata) {
            setDeriveMetadata(String.valueOf(metadata));
        }

        JSONObject jsonConfiguration= JSONObject.parseObject(this.configuration);

        jsonConfiguration.putAll(newConf);

        setConfiguration(jsonConfiguration.toJSONString());
    }


    public boolean hasFeature() {
        if (this.features == null) {
            return false;
        }
        return this.features.equals(1);
    }

}
